/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package pardal.algorithms;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import pardal.core.ManipulationHMC;
import pardal.core.Node;
import weka.classifiers.AbstractClassifier;
import weka.classifiers.Classifier;
import weka.core.Attribute;
import weka.core.Instances;

/**
 *
 * @author Valdigleis
 */
public class HMC {
    //O Classificador base
    private Classifier classifierBase;
    //A lista de classificadores usados no HMLP
    private Classifier classifiers[];
    //intânicas do problema
    private Instances instances;
    //Objeto de manipulação
    private ManipulationHMC mHMC;
    //Nodos com classificadores;
    private ArrayList<Node> nodes;
    //Founds para o cross-validation
    private int numberCross;
    //porcentagem para treinamento teste
    private double numberPercent;
    //marcador se a predição é até os nós folhas ou não.
    private boolean inLeaf;
    //valor do limiar para aborta a classificação nos nós não folhas
    private double limiar;
    //número de erros e acertos na classificação
    private int ok[], err[];
    //texto de resultados.
    private String textResult;

    public HMC(Classifier classifierBase, Instances instances, boolean inLeaf, double limiar) {
        try {
            this.classifierBase = classifierBase;
            this.instances = instances;
            this.mHMC = new ManipulationHMC(this.instances);
            this.nodes = this.mHMC.getNodesHMC();
            this.classifiers = AbstractClassifier.makeCopies(classifierBase, this.nodes.size());
            this.inLeaf = inLeaf;
            this.limiar = limiar;
            this.ok = new int[this.nodes.size()];
            this.err = new int[this.nodes.size()];
            for(int i = 0; i < this.nodes.size(); i++){
                this.ok[i] = 0;
                this.err[i] = 0;
            }
            runAll();
        } catch (Exception e) {
            e.printStackTrace();
        }
        
    }
    
    
    private void runAll(){
        bildClassifiersAll();
        testAll();
        /**
        for(int i = 0; i < this.ok.length; i++){
            System.out.println("Classificador " + i);
            System.out.println("Erro " + this.err[i]);
            System.out.println("Acertos " + this.ok[i]);
        }
        */
        createResult();
    }
    
    private void bildClassifiersAll(){
        String aux;
        this.textResult = "";
        for (int i = 0; i < this.nodes.size(); i++) {
            try {
                Instances train = this.mHMC.buildInstancesForNode(this.nodes.get(i));
                this.classifiers[i].buildClassifier(train);
                aux = "--------------------------\n" + 
                                  "Classifier in node: "         + this.nodes.get(i).getLabel() + "\n" + 
                                  "Number Example in Train: "    + train.numInstances() + "\n--------------------------\n";
                this.textResult = this.textResult.concat(aux);
            } catch (Exception ex) {
                ex.printStackTrace();
                
            }
        }
    }
    
    private void testAll(){
        try {
            if(this.inLeaf){
                String classeLocal, classeReal;
                double id;
                for(int i = 0; i < this.instances.numInstances(); i++){
                    classeReal = this.instances.instance(i).stringValue(this.instances.classIndex());
                    int j  =0;
                    while(true){
                        id = this.classifiers[j].classifyInstance(this.instances.instance(i));
                        classeLocal = getClasseInTest(id);
                        if(classeReal.equals(classeLocal) || classeReal.contains(classeLocal)){
                            this.ok[j]++;
                        }else{
                            this.err[j]++;
                        }
                        if(this.mHMC.nodeIsLeaf(classeLocal)){
                            break;
                        }else{
                            j = getIndexOfClasse(classeLocal);
                        }
                    }
                }
            }else{
                
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private String getClasseInTest(double id){
        int indexClasse = (int) id;
        Attribute att = this.instances.attribute(this.instances.classIndex());
        String classe = att.value(indexClasse);
        return classe;
    }
    
    private int getIndexOfClasse(String classe){
        int i = 0;
        for(int j = 0; j < this.nodes.size(); j++){
            if(this.nodes.get(j).getLabel().equals(classe)){
                i = j;
                break;
            }
        }
        return i;
    }
    
    
    private ArrayList<Integer> getIndexClassifiers(int level){
        ArrayList<Integer> index = new ArrayList<>();
        for(int i = 0; i < this.nodes.size(); i++){
            if(this.nodes.get(i).getLevel() == level){
                index.add(i);
            }
        }
        return index;
    }
    
    private void createResult(){
        int err, acc, examples, finalLevel;
        double percentAcc, percentErr;
        ArrayList<Integer> levels = new ArrayList<>();
        finalLevel = this.nodes.get(0).getLevel();
        levels.add(finalLevel);
        //pego os levels que tem nós com classificador
        for(int i = 0; i < this.nodes.size(); i++){
            if(finalLevel != this.nodes.get(i).getLevel()){
                finalLevel = this.nodes.get(i).getLevel();
                levels.add(finalLevel);
            }
        }
        String aux;
        this.textResult = this.textResult.concat("\n--------------------------\nResults in Classification \n");
        for(int i = 0; i < levels.size(); i++){
            err = 0; acc =0; examples = 0;
            aux = "\nClassifiers in level: " + levels.get(i);
            this.textResult = this.textResult.concat(aux);
            ArrayList<Integer> index = getIndexClassifiers(levels.get(i));
            for(int j = 0; j < index.size(); j++){
                err = err + this.err[(int)index.get(j)];
                acc = acc + this.ok[(int)index.get(j)];
            }
            examples = err + acc;
            percentAcc = (100* acc)/examples;
            percentErr = (100* err)/examples;
            aux = "\nExamples testing: " + examples +"\n"+
                  "Classification error: " + err + "\n" +
                  "Classification correct" + acc + "\n" +
                  "Percent of error: " + percentErr + "%\n" +
                  "Percent of correct: " + percentAcc + "%\n";
            this.textResult = this.textResult.concat(aux);
        }
        
    }

    public String getTextResult() {
        return textResult;
    }
    
}
